#include <mutex>
#include <string>
#include <fstream>
#include <vector>
#include <unordered_map>
#include <unistd.h>
#include <stdlib.h>
#include <memory>
#include "Log.hpp"
#include "util.hpp"

extern Log lg;
const std::string raw_path = "./data/raw_html/raw.bin";
std::mutex lock;

// 单例类
class Index
{
private:
    static Index *index;

public:
    // 获取单例
    static Index *GetInstance()
    {
        if (index == nullptr)
        {
            lock.lock();
            if (index == nullptr)
            {
                index = new Index();
            }
            lock.unlock();
        }
        lg(Info, "获取index单例成功\n");
        return index;
    }

    struct DocInfo_t
    {
        std::string title;
        std::string content;
        std::string url;
        uint64_t doc_id;
    };

    struct InvertedElem
    {
        std::string word;
        int weight;
        uint64_t doc_id;
    };

    using InvertedList = std::vector<InvertedElem>;
    void BuildIndex(const std::string &raw_path)
    {
        std::ifstream in(raw_path, std::ios::binary);
        if (!in.is_open())
        {
            lg(Fatal, "build index open file error\n");
        }
        std::string line;
        int count = 0;
        while (std::getline(in, line))
        {
            // 构建正排索引
            count++;
            // boost::to_lower(line);
            if (count % 50 == 0)
            {
                lg(Info, "构建第%d个索引\n", count);
            }
            // for debug
            // if (count == 7000)
            // {
            //     break;
            // }

            DocInfo_t *doc = BuildForwardIndex(line);
            if (doc == nullptr)
            {
                continue;
            }
            // 构建倒排索引
            if (!BuildInvertedIndex(doc))
            {
                continue;
            }
        }
        in.close();
    }
private:
    bool BuildInvertedIndex(DocInfo_t *doc)
    {
        // 1.jieba分词
        std::vector<std::string> title_words;
        std::vector<std::string> content_words;
        if (!ns_util::JiebaCutString::CutString(boost::to_lower_copy(doc->title), &title_words))
        {
            return false;
        }

        if (!ns_util::JiebaCutString::CutString(boost::to_lower_copy(doc->content), &content_words))
        {
            return false;
        }
        // 2.统计词频
        struct title_content_count
        {
            int title_count = 0;
            int content_count = 0;
        };
        std::unordered_map<std::string, title_content_count> words_frequency;
        for (auto &word : title_words)
        {
            words_frequency[word].title_count++;
        }
        for (auto &word : content_words)
        {
            words_frequency[word].content_count++;
        }
        // 3.自定义相关性
        #define X 10
        #define Y 1
        for (auto& pair : words_frequency)
        {
            std::string word = pair.first;
            title_content_count frequency = pair.second;
            InvertedElem elem;
            elem.word = word;
            elem.doc_id = doc->doc_id;
            elem.weight = X * frequency.title_count + Y * frequency.content_count; 
            inverted_index[word].push_back(std::move(elem));
        }
        return true;
    }

    DocInfo_t *BuildForwardIndex(const std::string &doc)
    {
        // 构建正排索引
        // 1.将doc划分为title，content，url三部分
        DocInfo_t docInfo;
        bool ret = ns_util::Split(doc, &(docInfo.title), &(docInfo.content), &(docInfo.url));
        if (!ret)
        {
            // 划分三部分失败,构建该doc的正排索引失败
            return nullptr;
        }
        // 2.将docInfo插入正排索引中
        docInfo.doc_id = forward_index.size();
        forward_index.push_back(std::move(docInfo));
        return &(forward_index[docInfo.doc_id]);
    }

public:
    // 通过doc_id获得文件信息
    DocInfo_t *GetFarwordIndex(uint64_t doc_id)
    {
        if (doc_id >= forward_index.size())
        {
            return nullptr;
        }
        return &(forward_index[doc_id]);
    }

    // 通过关键字获得倒排拉链
    InvertedList *GetInvertedIndex(const std::string &word)
    {
        if (inverted_index.count(word) == 0)
        {
            return nullptr;
        }
        return &(inverted_index[word]);
    }
    ~Index() = default;

private:
    Index() = default;
    Index(const Index &) = delete;
    Index &operator=(const Index &) = delete;

private:
    std::vector<DocInfo_t> forward_index;
    std::unordered_map<std::string, InvertedList> inverted_index;
};

Index* Index::index = nullptr;
